#include "logger.h"

Logger::Logger() {
    m_is_close = true;
    m_lines = 0;
    m_async_mode = false;
}

Logger::~Logger() {
    m_is_close = true;
    pthread_join(m_worker_id, nullptr);
    if (m_file != nullptr) {
        fflush(m_file);
        fclose(m_file);
        LOG_DEBUG("Log file closed.\n");
    }
    delete m_log_queue;

}

Logger *Logger::get_instance() {
    static Logger logger;
    return &logger;
}

bool Logger::init(const char *file_name, int split_lines,
                  int buf_size, int queue_capacity) {
    // 如果设置了阻塞队列则代表是异步模式
    if (queue_capacity > 0){
        m_async_mode = true;
        m_log_queue = new BlockQueue<string>(queue_capacity);
        pthread_create(&m_worker_id, nullptr, flush_worker, nullptr);
    }

    m_is_close = false;
    m_buf_size = buf_size;
    m_split_lines = split_lines;

    // 处理文件名
    time_t t = time(nullptr);
    struct tm log_time{};
    localtime_r(&t, &log_time);
    m_today = log_time.tm_mday;

    const char *slash_loc = strrchr(file_name, '/');
    char log_full_name[256] = {0};
    if (slash_loc == nullptr) {
        strcpy(m_log_name, file_name);
        strcpy(m_dir_name, "./");
    }
    else {
        strcpy(m_log_name, slash_loc + 1);
        strncpy(m_dir_name, file_name, slash_loc - file_name + 1);
    }

    return true;
}

void Logger::write_log(int level, const char *format, ...) {
    // 事件类型标签
    char lv_tag[16] = {0};
    switch (level) {
        case '0':
            strcpy(lv_tag, "[debug]");
            break;
        case 1:
            strcpy(lv_tag, "[info]:");
            break;
        case 2:
            strcpy(lv_tag, "[warn]:");
            break;
        case 3:
            strcpy(lv_tag, "[error]:");
            break;
        default:
            strcpy(lv_tag, "[info]:");
            break;
    }


    // 单行内容写入到缓冲队列
    struct timeval now = {0, 0};
    gettimeofday(&now, nullptr);
    time_t t = now.tv_sec;
    struct tm log_time{};
    localtime_r(&t, &log_time);    // localtime()函数不是线程安全的， 用localtime_r()！！

    char *buffer = new char[m_buf_size]{0};
    va_list va_lst;
    va_start (va_lst, format);
    int prefix = snprintf(buffer, 48, "%d-%02d-%02d %02d:%02d:%02d.%06ld %s ",
                          log_time.tm_year + 1900, log_time.tm_mon + 1, log_time.tm_mday,
                          log_time.tm_hour, log_time.tm_min, log_time.tm_sec, now.tv_usec, lv_tag);
    int content = vsnprintf(buffer + prefix, m_buf_size - 1, format, va_lst);
    buffer[prefix + content] = '\n';
    buffer[prefix + content + 1] = '\0';
    va_end(va_lst);

    string single_line = buffer;
    if (m_async_mode) {
        while(true) {
            if (!m_log_queue->full()){
                m_log_queue->push(single_line);
                break;
            }
        }
    }
    else {
        fputs(single_line.c_str(), m_file);
    }

    delete [] buffer;
}

void Logger::flush() {
    // 强制刷写流缓冲区
    m_file_lock.lock();
    fflush(m_file);
    m_file_lock.unlock();
}

void Logger::async_write() {
    // 异步写文件
    string single_line;
    while (!m_is_close) {
        if (m_log_queue->try_to_pop(single_line)) {
            // 时间与行数处理
            time_t t = time(nullptr);
            struct tm write_time{};
            localtime_r(&t, &write_time);

            m_file_lock.lock();
            m_lines += 1;
            if (write_time.tm_mday != m_today || m_lines % m_split_lines == 1) {
                // 时间不对应或行数超出log文件行数限制时，保存当前文件并新建文件
                char new_file_name[256] = {0};
                char new_time[16] = {0};
                snprintf(new_time, 16, "%d_%02d_%02d_",
                         write_time.tm_year + 1900, write_time.tm_mon + 1, write_time.tm_mday);
                if (write_time.tm_mday != m_today) {
                    // 新一天
                    m_lines = 1;
                    m_today = write_time.tm_mday;
                }
                snprintf(new_file_name, 255, "%s%s%s.%lld", m_dir_name, new_time,
                         m_log_name, m_lines / m_split_lines);

                //关闭旧文件/创建新文件
                if (m_file != nullptr) {
                    fflush(m_file);
                    fclose(m_file);
                }
                m_file = fopen(new_file_name, "a");
            }

            fputs(single_line.c_str(), m_file);
            fflush(m_file);
            m_file_lock.unlock();
        }
    }

}

void* Logger::flush_worker(void *arg) {
    Logger::get_instance()->async_write();
}

void Logger::close() {
    m_is_close = true;
}
